/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "sg_local.h"


odMemory_t *		odMemory = NULL;
odFileSystem_t *	odFileSystem = NULL;
odCmdSystem_t *		odCmdSystem = NULL;
odCVarSystem_t *	odCVarSystem = NULL;
odParser_t *		odParser = NULL;
odSystem_t *		odSystem = NULL;
odCollision_t *		odCollision = NULL;
odRenderer_t *		odRenderer = NULL;
odSound_t *			odSound = NULL;
odEditor_t *		odEditor = NULL;

levelLocals_t		level;

sgExport_t			sge;
sgImport_t *		sgi;

cvar_t *			sg_gameName;
cvar_t *			sg_gameDate;
cvar_t *			sg_protocol;
cvar_t *			sg_timeLimit;
cvar_t *			sg_friendlyFire;
cvar_t *			sg_filterBan;


/*
 ==================
 SG_RunFrame
 ==================
*/
static void SG_RunFrame (int time){

	entity_t	*entity;
	int			i;

	level.time = time;

	// Let entities think
	for (i = 0; i < MAX_ENTITIES; i++){
		entity = level.entities[i];
		if (!entity)
			continue;

		if (entity->Think)
			entity->Think(entity);
	}

}

/*
 ==================
 SG_SpawnEntities
 ==================
*/
static void SG_SpawnEntities (const char *levelName, int levelTime){

	char		name[MAX_OSPATH];
	script_t	*script;
	token_t		token;
	entity_t	*entity;

	// Load the clip map
	Str_Copy(name, levelName, sizeof(name));
	Str_DefaultFileExtension(name, sizeof(name), ".bsp");

	odSystem->Printf("Loading map '%s'...\n", name);

	odCollision->LoadMap(name);

	// Set config strings
	level.configStrings[CS_LEVEL_NAME] = odMemory->CopyString(levelName, TAG_SERVER);

	// Create the clip sectors
	SG_InitClipSectors();

	// Parse and spawn all the entities
	Str_Copy(name, levelName, sizeof(name));
	Str_DefaultFileExtension(name, sizeof(name), ".ent");

	odSystem->Printf("Spawning entities...\n", name);

	script = odParser->LoadScriptFile(name);
	if (!script)
		odSystem->Error(false, "SG_SpawnEntities: no entities");

	odParser->SetScriptFlags(script, SF_NOFATALERRORS | SF_ALLOWESCAPECHARS);

	while (1){
		if (!odParser->ReadToken(script, &token))
			break;		// End of data

		if (token.type != TT_PUNCTUATION || Str_Compare(token.string, "{")){
			odParser->ScriptError(script, "expected '{', found '%s' instead", token.string);
			break;
		}

		odParser->UnreadToken(script, &token);

		// Allocate a new entity
		entity = SG_AllocEntity();

		// Spawn the entity
		if (!SG_SpawnEntity(entity, script))
			SG_FreeEntity(entity);

		// TODO: first entity should be worldSpawn

	}

	odParser->FreeScript(script);

	// Set the level time
	level.time = levelTime;

	// Spawned all entities
	level.spawned = true;
}

/*
 ==================
 SG_SetupBaselines
 ==================
*/
static void SG_SetupBaselines (void){

	entity_t	*entity;
	int			i;

	for (i = 0; i < MAX_ENTITIES; i++){
		entity = level.entities[i];
		if (!entity)
			continue;

		// Take current state as baseline
		entity->baseline = entity->state;
	}
}

/*
 ==================
 SG_Init
 ==================
*/
cvar_t *sg_noclipSpeed;
cvar_t *sg_moveSpeed;
cvar_t *sg_gravity;
static void SG_Init (void){

	odSystem->Printf("-------- Game Initialization --------\n");
	odSystem->Printf("gamename: %s\n", GAME_NAME);
	odSystem->Printf("gamedate: %s\n", __DATE__);

	// Set the byte swapping variables in a portable manner
	Swap_Init();

	// Set an initial random seed
	M_RandSeed(odSystem->Milliseconds());

	// Register our variables and commands
	sg_gameName = odCVarSystem->Get("sg_gameName", GAME_NAME, CVAR_SERVERINFO | CVAR_ROM, "Game name");
	sg_gameDate = odCVarSystem->Get("sg_gameDate", __DATE__, CVAR_ROM, "Game date");
	sg_protocol = odCVarSystem->Get("sg_protocol", Str_FromInteger(GAME_PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_ROM, "Game protocol version");
	sg_timeLimit = odCVarSystem->Get("sg_timeLimit", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, "Time limit in minutes");
	sg_friendlyFire = odCVarSystem->Get("sg_friendlyFire", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, "Friendly fire");
	sg_filterBan = odCVarSystem->Get("sg_filterBan", "1", CVAR_ARCHIVE, "Filter banned IPs");

	// TODO!!!
	sg_noclipSpeed = odCVarSystem->Get("sg_noclipSpeed", "400", 0, NULL);
	sg_moveSpeed = odCVarSystem->Get("sg_moveSpeed", "200", 0, NULL);
	sg_gravity = odCVarSystem->Get("sg_gravity", "800", 0, NULL);

	// Clear all the level locals
	memset(&level, 0, sizeof(levelLocals_t));

}

/*
 ==================
 SG_Shutdown
 ==================
*/
static void SG_Shutdown (void){

	SG_ShutdownClipSectors();

	// Free all memory
	odMemory->TagFree(TAG_SERVER);
}


// ============================================================================


/*
 ==================
 GetSGameAPI

 This is the only function exported by the game module
 ==================
*/
sgExport_t *GetSGameAPI (sgImport_t *import){

	sgi = import;

	odMemory					= &import->odMemory;
	odFileSystem				= &import->odFileSystem;
	odCmdSystem					= &import->odCmdSystem;
	odCVarSystem				= &import->odCVarSystem;
	odParser					= &import->odParser;
	odSystem					= &import->odSystem;
	odCollision					= &import->odCollision;
	odEditor					= &import->odEditor;

	// Set the exported functions
	sge.apiVersion				= SGAME_API_VERSION;

	sge.gameProtocol			= GAME_PROTOCOL_VERSION;

	sge.ClientConnect			= SG_ClientConnect;
	sge.ClientDisconnect		= SG_ClientDisconnect;
	sge.ClientUserInfoChanged	= SG_ClientUserInfoChanged;
	sge.ClientBegin				= SG_ClientBegin;
	sge.ClientCommand			= SG_ClientCommand;
	sge.ClientThink				= SG_ClientThink;
	sge.ClientWriteGameState	= SG_ClientWriteGameState;
	sge.ClientWriteSnapshot		= SG_ClientWriteSnapshot;
	sge.RunFrame				= SG_RunFrame;
	sge.SpawnEntities			= SG_SpawnEntities;
	sge.SetupBaselines			= SG_SetupBaselines;
	sge.Init					= SG_Init;
	sge.Shutdown				= SG_Shutdown;

	return &sge;
}
